package com.cenluan.qq;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Date;
import java.util.Random;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.cenluan.common.Const;
import com.cenluan.common.Profiles;
import com.cenluan.kit.IOKit;
import com.cenluan.kit.RequestKit;
import com.cenluan.kit.UUIDKit;
import com.cenluan.login.ClearLoginInterceptor;
import com.cenluan.session.SessionManager;
import com.cenluan.user.User;
import com.jfinal.core.Controller;
import com.jfinal.kit.StrKit;

/**
 * qq
 * 
 * @author peak
 * 
 */
public class QQController extends Controller {
	/**
	 * auth授权接口的参数值，跳转链接时放入session，回调时验证
	 */
	private static final String SESSION_STATE = "QQ_STATE";

	/**
	 * 跳转去登录
	 * 
	 * @throws UnsupportedEncodingException
	 */
	@ClearLoginInterceptor
	public void toLogin() throws UnsupportedEncodingException {
		String url = "https://graph.qq.com/oauth2.0/authorize";
		url += "?response_type=code";
		url += "&client_id=" + Profiles.getQqAppId();
		url += "&redirect_uri="
				+ URLEncoder.encode(RequestKit.getUrlPrefix(getRequest())
						+ "/qq/login", "UTF-8");
		// 随机生成一个state，放入session中
		Random random = new Random();
		int state = random.nextInt();
		setSessionAttr(SESSION_STATE, Integer.toString(state));
		url += "&state=" + state;
		// 如果是手机就会传入display参数为mobile
		if ("mobile".equals(getPara("display"))) {
			url += "&display=mobile";
		}

		redirect(url);
	}

	/**
	 * 登录回调
	 * 
	 * @throws IOException
	 * @throws MalformedURLException
	 */
	public void login() throws IOException {
		String state = getPara("state");
		if (!state.equals(getSessionAttr(SESSION_STATE))) {
			renderError(403);
			return;
		}
		if (isParaExists("msg")) {
			renderText("登录失败：原因： " + getPara("msg"));
			return;
		}
		// 获取access token
		String code = getPara("code");
		JSONObject json = getAccessToken(code,
				RequestKit.getUrlPrefix(getRequest()) + "/qq/login");
		// 获取openid
		String openid = getOpenid(json.getString("access_token"));
		// 依据openid查找用户
		User user = User.dao.findByQqOpenId(openid);
		if (user == null) {
			// 获取QQ用户信息
			JSONObject data = getUserInfo(json.getString("access_token"),
					openid);
			user = new User();
			user.set("id", UUIDKit.generateUUID());
			user.set("qqOpenId", openid);
			user.set("registerTime", new Date());
			if (data.containsKey("figureurl_qq_2")
					&& StrKit.notBlank(data.getString("figureurl_qq_2"))) {
				user.set("avatarUrl", data.getString("figureurl_qq_2"));
			} else {
				user.set("avatarUrl", data.getString("figureurl_qq_1"));
			}
			user.set("nickname", data.getString("nickname"));
			user.set("loginTime", new Date());
			user.set("loginIp", RequestKit.getClientIp(getRequest()));
			user.set("sessionId", getSession().getId());
			user.save();
		} else {
			SessionManager.invalidate(user.getStr("sessionId"));
			user.set("loginTime", new Date());
			user.set("loginIp", RequestKit.getClientIp(getRequest()));
			user.set("sessionId", getSession().getId());
			user.keep("id", "loginTime", "loginIp", "sessionId").update();
			user = User.dao.findById(user.getStr("id"));
		}
		setSessionAttr(Const.SESSION_USER, user);
		String referer = getSessionAttr(Const.SESSION_REFERER);
		if (referer != null) {
			removeSessionAttr(Const.SESSION_REFERER);
			redirect(referer);
			return;
		}
		redirect("/");

	}

	/**
	 * 获取access token
	 * 
	 * @param code
	 *            地址回调时传回的code
	 * @param redirectUri
	 *            回调uri地址
	 * @return 错误的返回结果里包含code和msg字段，正确结果参数说明<br>
	 *         access_token 授权令牌，Access_Token。<br>
	 *         expires_in 该access token的有效期，单位为秒。<br>
	 *         refresh_token 在授权自动续期步骤中，获取新的Access_Token时需要提供的参数。
	 * @throws IOException
	 */
	private JSONObject getAccessToken(String code, String redirectUri)
			throws IOException {
		String url = "https://graph.qq.com/oauth2.0/token";
		url += "?grant_type=authorization_code";
		url += "&client_id=" + Profiles.getQqAppId();
		url += "&client_secret=" + Profiles.getQqAppKey();
		url += "&code=" + code;
		url += "&redirect_uri=" + URLEncoder.encode(redirectUri, "UTF-8");
		JSONObject json = JSON.parseObject(IOKit.iptToStr(new URL(url)
				.openStream()));
		if (json.containsKey("code")) {
			throw new RuntimeException("调用qq接口，获取access token出错，code:"
					+ json.getString("code") + "，msg:" + json.getString("msg"));
		}
		return json;
	}

	/**
	 * 获取openid，程序使用的是qq登录的pc版接口，获取openid的接口返回的是jsonp数据，这里直接解析出结果，jsonp返回结果示例：<br>
	 * callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} );
	 * 
	 * @param accessToken
	 * @return
	 * @throws IOException
	 */
	private String getOpenid(String accessToken) throws IOException {
		URL url = new URL("https://graph.qq.com/oauth2.0/me?access_token="
				+ accessToken);
		String result = IOKit.iptToStr(url.openStream());
		result = result.substring(result.indexOf("{"),
				result.lastIndexOf("}") + 1);
		JSONObject json = JSON.parseObject(result);
		return json.getString("openid");
	}

	/**
	 * 获取qq用户信息
	 * 
	 * @param accessToken
	 * @param openid
	 * @return 返回结果示例说明：<br>
	 *         ret 返回码 msg 如果ret<0，会有相应的错误信息提示，返回数据全部用UTF-8编码。 <br>
	 *         nickname 用户在QQ空间的昵称。 <br>
	 *         figureurl 大小为30×30像素的QQ空间头像URL。<br>
	 *         figureurl_1 大小为50×50像素的QQ空间头像URL。<br>
	 *         figureurl_2 大小为100×100像素的QQ空间头像URL。 <br>
	 *         figureurl_qq_1 大小为40×40像素的QQ头像URL。 <br>
	 *         figureurl_qq_2 大小为100×100像素的QQ头像URL。<br>
	 *         需要注意，不是所有的用户都拥有QQ的100x100的头像，但40x40像素则是一定会有。<br>
	 *         gender 性别。 如果获取不到则默认返回"男" <br>
	 *         is_yellow_vip 标识用户是否为黄钻用户（0：不是；1：是）。<br>
	 *         vip 标识用户是否为黄钻用户（0：不是；1：是） <br>
	 *         yellow_vip_level 黄钻等级 level 黄钻等级 <br>
	 *         is_yellow_year_vip 标识是否为年费黄钻用户（0：不是； 1：是）
	 * @throws IOException
	 */
	private JSONObject getUserInfo(String accessToken, String openid)
			throws IOException {
		String url = "https://graph.qq.com/user/get_user_info";
		url += "?access_token=" + URLEncoder.encode(accessToken, "UTF-8");
		url += "&oauth_consumer_key="
				+ URLEncoder.encode(Profiles.getQqAppId(), "UTF-8");
		url += "&openid=" + URLEncoder.encode(openid, "UTF-8");
		JSONObject json = JSON.parseObject(IOKit.iptToStr(new URL(url)
				.openStream()));
		if (json.getInteger("ret") < 0) {
			throw new RuntimeException("调用qq用户信息接口失败：" + json.getString("msg"));
		}
		return json;
	}
}
